package com.txby.dplus.common.utils.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

import com.txby.dplus.common.annotation.MysqlAutoPK;

/**
 * POJO基类 用户反射实体注解 包括属性字段和参数数据的对应关系
 */
public class MyBatisReflectUtil {
	private transient static String newId = null;
	private transient static boolean isAutoId;
	/**
	 * 用于存放POJO的列信息
	 */
	private transient static Map<Class<?>, List<String>> columnMap = new HashMap<Class<?>, List<String>>();
	/**
	 * 默认为实体所有性字段，请保证和数据表对应一致
	 */
	private boolean isAllField = true;
	/*
	 * 为注解字段的数据
	 */
	private transient static Map<String, Class<?>> columnAnnotationMap = new HashMap();
	/**
	 * 当前对象实体
	 */
	private transient static Object o;

	private Object getObject() {
		return this.o;
	}
	/**
	 * 获取POJO对应的表名 需要POJO中的属性定义@Table(name)
	 * @return
	 */
	public static String tablename(Object o) {
		Table table = o.getClass().getAnnotation(Table.class);
		if (table != null)
			return table.name();
		else
			throw new PojoStructureException("undefine POJO @Table, need Tablename(@Table(name))");
	}
	public static String tablename(Class clazz) {
		// Annotation an=clazz.getAnnotation(Table.class);
		Table table = (Table) clazz.getAnnotation(Table.class);
		if (table != null)
			return table.name();
		else
			throw new PojoStructureException("undefine POJO @Table, need Tablename(@Table(name))");
	}
	public MyBatisReflectUtil() {

	}

	public MyBatisReflectUtil(boolean isAllField) {
		this.isAllField = isAllField;

	}
	/**
	 * 获取POJO中的主键字段名 需要定义@Id
	 * 
	 * @return
	 */
	public static String id(Object o) {
		Map map = getAllField(o);
		if (map.get("objIdmysql") != null) {
			return map.get("objIdmysql").toString();
		} else if (map.get("objId") != null) {
			return map.get("objId").toString();
		}
		throw new RuntimeException("undefine POJO @Id");
	}

	private boolean isNull(Object o, String fieldname) {
		try {
			Map map = getAllField(o);
			List<Field> fields = null;
			Field field = null;
			if (map.get("fields") != null) {
				fields = (List) map.get("fields");
				for (Field fd : fields) {
					if (fd.getName() != null && fd.getName().equals(fieldname)) {
						field = fd;
					}
				}
			}
			return isNull(o, field);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}

	private boolean isNull(Object o, Field field) {
		try {
			// field.setAccessible(true);
			if (field == null) {
				return true;
			} else {
				return PropertyUtils.getProperty(o, field.getName()) == null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}
	/**
	 * 初始实体的属性字段，用于计算类定义 需要POJO中的属性定义@Column(name)
	 */
	public void caculationColumnList(Object o) {
		this.o = o;
		if (columnMap.containsKey(o.getClass()))
			return;
		// Field[] fields = o.getClass().getDeclaredFields();
		List<Field> fields = new ArrayList<>();
		List<String> columnList = new ArrayList<String>(fields.size());
		Class tempClass = o.getClass();
		/*
		 * while (tempClass !=
		 * null&&!tempClass.getName().toLowerCase().equals("java.lang.object"))
		 * {//当父类为null的时候说明到达了最上层的父类(Object类).
		 * fields.addAll(Arrays.asList(tempClass .getDeclaredFields()));
		 * tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己 }
		 */
		Map<String, Class> idAnnotation = new HashMap();
		Map<String, Class> mysqlIdAnnotation = new HashMap();
		while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {// 当父类为null的时候说明到达了最上层的父类(Object类).
			Field fd[] = tempClass.getDeclaredFields();
			fields.addAll(Arrays.asList(fd));
			tempClass = tempClass.getSuperclass(); // 得到父类,然后赋给自己
			for (Field field : fd) {
				Annotation an = field.getAnnotation(Id.class);
				Annotation mysqlAnnotation = field.getAnnotation(MysqlAutoPK.class);
				if (an != null) {
					GeneratedValue gv = field.getAnnotation(GeneratedValue.class);
					if (gv == null || !gv.strategy().equals(GenerationType.IDENTITY)) {
						idAnnotation.put(field.getName(), Id.class);
					}
				}
				if (mysqlAnnotation != null) {
					mysqlIdAnnotation.put(field.getName(), MysqlAutoPK.class);
				}
			}
		}
		for (Field field : fields) {
			// 如果含有javax.persistence.Transient @Transient ，该字段将不做为持久化数据库操作
			if (field.isAnnotationPresent(Transient.class)) {
				continue;
			}
			if (mysqlIdAnnotation.get(field.getName()) != null) {
				columnList.add(field.getName());
				columnAnnotationMap.put(field.getName(), MysqlAutoPK.class);
			} else if (idAnnotation.get(field.getName()) != null) {
				columnList.add(field.getName());
				columnAnnotationMap.put(field.getName(), Id.class);
			} else if (field.isAnnotationPresent(Column.class)) {
				columnList.add(field.getName());
				columnAnnotationMap.put(field.getName(), Column.class);
			} else if (isAllField) {
				// 下面控制实体所有属性的添加
				if (!field.getName().equals("serialVersionUID")) {
					columnList.add(field.getName());
					columnAnnotationMap.put(field.getName(), Column.class);
				}
			}
		}
		columnMap.put(o.getClass(), columnList);
	}
	/**
	 * 获取用于WHERE的 有值字段表
	 * @return
	 */
	public List<WhereColumn> returnWhereColumnsName(Object o) {
		Field[] fields = o.getClass().getDeclaredFields();
		List<WhereColumn> columnList = new ArrayList<WhereColumn>(fields.length);
		for (Field field : fields) {
			// if((field.isAnnotationPresent(Column.class) && !isNull(o,field)))
			if (!isNull(o, field))
				columnList.add(new WhereColumn(field.getName(), field.getGenericType().equals(String.class)));
		}
		return columnList;
	}
	/**
	 * Where条件信息
	 */
	public class WhereColumn {
		public String name;
		public boolean isString;
		public WhereColumn(String name, boolean isString) {
			this.name = name;
			this.isString = isString;
		}
	}
	/**
	 * 用于获取Insert的字段累加
	 * 
	 * @return
	 */
	public String returnInsertColumnsName(Object o) {
		StringBuilder sb = new StringBuilder();
		List<String> list = columnMap.get(o.getClass());
		int i = 0;
		for (String column : list) {
			boolean isId = false;
			// System.out.println("长"+columnAnnotationMap.get(column).getName());

			// 增加mysql自增整型主键的处理
			if (columnAnnotationMap.get(column).getName().equals(MysqlAutoPK.class.getName())) {
				continue;
			} else if (columnAnnotationMap.get(column).getName().equals(Id.class.getName())) {
				// System.out.println("长"+c2
				if (isNull(o, column)) {
					isId = true;
				}
			} else if (isNull(o, column)) {
				continue;
			}
			if (i++ != 0)
				sb.append(',');
			sb.append(column);
		}
		return sb.toString();
	}

	/**
	 * 用于获取Insert的字段映射累加
	 * 
	 * @return
	 */
	public String returnInsertColumnsDefine(Object o) {
		StringBuilder sb = new StringBuilder();
		List<String> list = columnMap.get(o.getClass());
		int i = 0;
		for (String column : list) {
			boolean isId = false;
			// System.out.println("长"+columnAnnotationMap.get(column).getName());
			if (columnAnnotationMap.get(column).getName().equals(MysqlAutoPK.class.getName())) {
				continue;

			} else if (columnAnnotationMap.get(column).getName().equals(Id.class.getName())) {
				// System.out.println("长"+column);
				if (isNull(o, column)) {
					isId = true;
				} else {

					try {
						newId = BeanUtils.getProperty(o, column);
					} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} else if (isNull(o, column)) {
				continue;
			}

			if (i++ != 0)
				sb.append(',');
			if (isId) {
				newId = IdWorker.get50UUID();
				isAutoId = true;
				sb.append("'" + newId + "'");
				// newId=BeanUtils.copyProperty(o, name, value);
			} else {
				sb.append("#{").append("pojo." + column).append('}');
			}

		}

		return sb.toString();
	}

	/**
	 * 用于查询时返回的字段如：id,name,age
	 * 
	 * @Description: TODO(描述)
	 * @param 设定
	 * @author 作者 jacking
	 * @date 2017-6-16 下午4:08:31
	 * @return
	 */
	public String returnColumnsDefine(Object o) {
		StringBuilder sb = new StringBuilder();
		List<String> list = columnMap.get(o.getClass());
		int i = 0;
		for (String column : list) {
			if (i++ != 0) {
				sb.append(',' + column);
			} else {
				sb.append(column);
			}
		}
		// System.out.println("长"+sb.toString());
		return sb.toString();
	}

	/**
	 * 用于获取Update Set的字段累加
	 * 
	 * @return
	 */
	public String returnUpdateSet(Object o) {
		StringBuilder sb = new StringBuilder();
		List<String> list = columnMap.get(o.getClass());
		int i = 0;
		Map map = getAllField(o);
		for (String column : list) {
			if (map.get("objIdmysql") != null && map.get("objIdmysql").toString().equals(column)) {
				continue;
			} else if (map.get("objId") != null && map.get("objId").toString().equals(column)) {
				continue;
			}else if (isNull(o, column)) {
				continue;
			}
			if (i++ != 0)
				sb.append(',');
			sb.append(column).append("=#{pojo.").append(column).append('}');
		}
		return sb.toString();
	}

	/**
	 * 打印类字段信息
	 */
	@Override
	public String toString() {
		Field[] fields = o.getClass().getDeclaredFields();
		StringBuilder sb = new StringBuilder();
		sb.append('[');
		for (Field f : fields) {
			if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers()))
				continue;
			Object value = null;
			try {
				f.setAccessible(true);
				value = f.get(this);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			if (value != null)
				sb.append(f.getName()).append('=').append(value).append(',');
		}
		sb.append(']');

		return sb.toString();
	}

	public static Map getAllField(Object o) {
		Map dataMap = new HashMap();
		Class tempClass = o.getClass();
		List<Field> fields = new ArrayList<>();
		String objId = null;
		String objIdmysql = null;
		while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {// 当父类为null的时候说明到达了最上层的父类(Object类).
			Field fd[] = tempClass.getDeclaredFields();
			fields.addAll(Arrays.asList(fd));
			tempClass = tempClass.getSuperclass(); // 得到父类,然后赋给自己
			for (Field field : fd) {
				Annotation an = field.getAnnotation(Id.class);
				Annotation mysqlautoid = field.getAnnotation(MysqlAutoPK.class);
				if (an != null) {
					GeneratedValue gv = field.getAnnotation(GeneratedValue.class);
					if (gv == null || !gv.strategy().equals(GenerationType.IDENTITY)) {
						objId = field.getName();
					}
				}
				if (mysqlautoid != null) {
					objIdmysql = field.getName();
				}
			}
		}

		dataMap.put("fields", fields);
		dataMap.put("objId", objId);
		dataMap.put("objIdmysql", objIdmysql);
		return dataMap;
	}

	public static String getNewId() {
		return newId;
	}

	public static void setNewId(String newId) {
		MyBatisReflectUtil.newId = newId;
	}

	/**
	 * 以下为一些分页信息,如不需要可以删除
	 */
	private int page;
	private int pageSize;

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public static boolean isAutoId() {
		return isAutoId;
	}

	public static void setAutoId(boolean isAutoId) {
		MyBatisReflectUtil.isAutoId = isAutoId;
	}

	public static Map<Class<?>, List<String>> getColumnMap() {
		return columnMap;
	}

	public static void setColumnMap(Map<Class<?>, List<String>> columnMap) {
		MyBatisReflectUtil.columnMap = columnMap;
	}

	public boolean isAllField() {
		return isAllField;
	}

	public void setAllField(boolean isAllField) {
		this.isAllField = isAllField;
	}

	public static Map<String, Class<?>> getColumnAnnotationMap() {
		return columnAnnotationMap;
	}

	public static void setColumnAnnotationMap(Map<String, Class<?>> columnAnnotationMap) {
		MyBatisReflectUtil.columnAnnotationMap = columnAnnotationMap;
	}

	public static Object getO() {
		return o;
	}

	public static void setO(Object o) {
		MyBatisReflectUtil.o = o;
	}

}